#include <iostream>
#include <vector>
#include <time.h>
#include <string>
#include <algorithm>
#include <iomanip>
#include <sstream>
#include <cmath>
#include <stdio.h>
#include <fstream>
using namespace std;


#define CORE_NUM 4 // number of processors .
#define TASK_NUM 5 // number of tasks in task set.



 //============== DECLARE GLOBAL VARIABLES ============================//

struct population // Structure holds the population.
{
    vector <string> chromosome;
    vector <double> fitness;
    vector <int> FLM;
};

double oldvalue[500];
double MUTATE_RATE = 0.5; // Mutation rate.
int iterr = 60; // number of iterations.

int rowA = 0;
int colA = 0;
double arrayA[15][15] = {{0}};


//==================== DECLARE FUNCTIONS =================================//

population Init_Population(population); // Create first population.

population Fitness_Calc(population); // Calculate fitness.

population Crossover(population pop); // Cross over of parents.

string roulette(population pop); // Roulette picking of parents.

string mutate(string chromo); // Mutate chromosome.

void Print_Stuff(population); // Print generations.

void MBFF(double *cs, double *ts, double *sr); // Declare the function.

int ReadFile(); // Read the data file.

void resetter(); // Reset global variables.

//double S_RATE[5] = {0.15, 0.4, 0.6, 0.8, 1.0}; // Speed rates of Xscale.
double S_RATE[5]  = {0.4, 0.6, 0.8, 1.0, 1.2};

double C_UTIL[CORE_NUM]; // Utilization of each processor after allocation.
double C_RATE[CORE_NUM]; // Speed rate of processor after each allocation.
double T_UTIL[TASK_NUM]; // Utilization of each task.
int T_CORE_NUM[TASK_NUM];// Location of each task in task set.
double P[5] = {80, 170, 400, 900, 1600}; // Power per speed rate for xscale.
//double xscale[5]  = {0.15, 0.4, 0.6, 0.8, 1.0};
double xscale[5]  = {0.4, 0.6, 0.8, 1.0, 1.2};


//============================ MAIN FUNCTION =============================//
int main()
{
    float ambffold = 0;
    double csize[CORE_NUM]; // number of cores.
    ReadFile();
    float averagefit = 0.0;
    float totalsetfit = 0.0;
    int cnt = 1;
for (int lol = 0; lol < 1; lol++){
    double tsize[rowA];
    for (int j = 0; j<colA; j++)
    {
        for (int i = 0; i < rowA; i++)
        {
            tsize[i] = arrayA[j][i];
        }

        //sort(tsize, tsize+rowA, greater<double>());

        cout << "\nALLOCATION TEST " << cnt << endl;
        MBFF(csize, tsize, xscale);


        population pop;
        pop = Init_Population(pop);
        pop = Fitness_Calc(pop);

        for (int i = 0; i < iterr; i++)
        {
            pop = Crossover(pop);
            pop = Fitness_Calc(pop);
            //Print_Stuff(pop);

        }

            cout<< "\nBEST CHROMOSOME: " << pop.chromosome[pop.chromosome.size()-1] << endl;
            cout << "BEST SAVING: " << pop.fitness[pop.fitness.size()-1] <<"mW    "<<pop.FLM[pop.FLM.size()-1]<<"%"<< endl;
            ambffold = ambffold + oldvalue[pop.fitness.size()-1];
            if (pop.FLM[pop.FLM.size()-1] >= 0)
            averagefit = averagefit + pop.FLM[pop.FLM.size()-1];
            cnt++;

            // RESET EVERYTHING FOR NEXT SET OF TASKS.
            pop.chromosome.erase(pop.chromosome.begin(), pop.chromosome.end());
            pop.fitness.erase(pop.fitness.begin(), pop.fitness.end());
            pop.FLM.erase(pop.FLM.begin(), pop.FLM.end());
            resetter();
    }

    totalsetfit = totalsetfit+averagefit;
    //cout << "Average power saving for this test set: "<< averagefit << "%" << endl;
}

    cout << "Average MBFF save: " << ambffold/50 << endl;
       cout << "total set average save: " << averagefit/45 << "%" << endl;
    return 0;
}

//========================================= INITIATE POPULATION ===============================//

population Init_Population(population pop)
{
    srand(time(NULL));
    int pop_size = 0;
    for (int t = 0; t < TASK_NUM; t++)
        pop_size = pop_size + int(T_UTIL[t]/0.01);

    double x, y;
    for (int TASK = 0; TASK < 5; TASK++) // For each task in task set do..
    {
        int t1 = int(T_UTIL[TASK]/0.01);  // This gives how many ways the task can be split given smallest 0.01.
        x = 0.0;
        for (int k = 0; k < t1; k++)
        {
            y = T_UTIL[TASK] - x; // Get split portion of given task.
            int RANDOM_CORE = (rand()% 4); // Randomly generate a processor number.
            int RANDOM_RATE = (rand()% 5); // randomly generate a speed rate.
            char buff[15];
            snprintf(buff, sizeof(buff),"%d %.2f %d %d", TASK, y, RANDOM_CORE, RANDOM_RATE); // Create chromosome.
            string str = buff;
            pop.chromosome.push_back(str);
            pop.fitness.push_back(0);
            pop.FLM.push_back(0);


            x = x + 0.01;
        }
    }
    return pop; // Return the population.
}


//====================================== FITNESS CALCULATION ==========================================//

population Fitness_Calc(population pop)
{
    int POP_SIZE = pop.chromosome.size(); // Get the size of incoming population.

    for (int i = 0; i < POP_SIZE; i++) // for each chromosome in population do...
    {
        string cs = pop.chromosome[i];
        stringstream ss (cs);
        int TASK, RANDOM_CORE, RANDOM_RATE; double Ui1;
        ss >> TASK >> Ui1 >> RANDOM_CORE >> RANDOM_RATE;

        int m = T_CORE_NUM[TASK] - 1;
        double Sy = S_RATE[RANDOM_RATE];
        double Sx = C_RATE[m];
        double Uj = C_UTIL[RANDOM_CORE];
        double Ui = T_UTIL[TASK];
        double Ui2 = Ui - Ui1;
        double Syo = C_RATE[RANDOM_CORE];
        double Ucx = C_UTIL[m];

        int Syn = distance(S_RATE, find(S_RATE, S_RATE + 5, Sy));
        int Sxn = distance(S_RATE, find(S_RATE, S_RATE+5, Sx));
        int Syoo = distance(S_RATE, find(S_RATE, S_RATE+5, Syo));

        double R = (Sx*(Sx-Ui))/(Sx-Sy);
        double L = (Sy - Uj);
        double y =  min(L,R); //

        double Pnew = (((Uj + Ui1)/Sy) * P[Syn]) + (((Ucx-Ui1)/Sx)*P[Sxn]);
        double Pold = ((Uj/Syoo)*P[Syoo]) + ((Ucx/Sx)*P[Sxn]);
        double diff = Pold-Pnew;
        int savings = int((diff/Pold)*100);

            if (y <= Ui1 && y > 0.00 && diff > 0 )
            {
                pop.fitness.at(i) = diff;
                pop.FLM.at(i) = savings;
                oldvalue[i] = Pold;

            }
            else
            {
                pop.fitness.at(i) = 10;
                oldvalue[i] = Pold;

            }
    }

    return pop;
}


//============================== CROSSOVER ====================================//

population Crossover(population pop)
{
    population pop2;
    int POP_SIZE = pop.chromosome.size();
    string parentA;
    string parentB;
    string child;
    string mutant;
    for (int i = 0; i < POP_SIZE; i++)
    {
        parentA = roulette(pop);
        parentB = roulette(pop);

        stringstream ss (parentA);
        int TASK1, RANDOM_CORE1, RANDOM_RATE1; double Ui11;
        ss >> TASK1 >> Ui11 >> RANDOM_CORE1 >> RANDOM_RATE1;

        stringstream ss2 (parentB);
        int TASK2, RANDOM_CORE2, RANDOM_RATE2; double Ui12;
        ss2 >> TASK2 >> Ui12 >> RANDOM_CORE2 >> RANDOM_RATE2;

        int x = (rand()% 100);

        if (x % 2 == 0)
        {
            char buff[15];
            snprintf(buff, sizeof(buff),"%d %.2f %d %d", TASK1, Ui11, RANDOM_CORE2, RANDOM_RATE2);
            child = buff;
        }
        else
        {
            char buff[15];
            snprintf(buff, sizeof(buff),"%d %.2f %d %d", TASK2, Ui12, RANDOM_CORE1, RANDOM_RATE1);
            child = buff;
        }

        mutant = mutate(child);
        pop2.chromosome.push_back(mutant);
        pop2.fitness.push_back(0);
        pop2.FLM.push_back(0);
    }

   return pop2;
}

//========================== ROULETTE WHEEL FOR PARENT SELECTION ===============================//

string roulette(population pop)
{
    double tot_fitness = 0;

    for (int i = 0; i < pop.fitness.size(); i++)
    {
       tot_fitness = tot_fitness + pop.fitness[i];
    }

    int POP_SIZE = pop.chromosome.size();
    double RAND_NUM = ((double)rand()/(RAND_MAX+1));
    double slice = (double)(RAND_NUM*tot_fitness);
    double FitSoFar = 0.0;

    for (int i = 0; i < POP_SIZE; i++)
    {
        FitSoFar += pop.fitness[i];
        if(FitSoFar >= slice)
        {
            return pop.chromosome[i];
        }
    }

    return "";
}


//==================================== PRINT DATA =========================================//

void Print_Stuff(population pop)
{
    int POP_SIZE = pop.chromosome.size();

    cout << setprecision(2) << fixed;
    cout << "#chrom" << "\t" << "Chromosome" << "\t" << "Fitness" << endl;
    for (int i = 0; i < POP_SIZE; i++)
    {
        cout << i << "\t" << pop.chromosome[i] << "\t" << pop.fitness[i] << "\t"<< pop.FLM[i] << endl;
    }

}


//==================================== MUTATE CHILD ===========================================//
string mutate(string chromo)
{
    int c, r;
    float x = ((float)rand()/(RAND_MAX+1));

    if ( x < MUTATE_RATE)
    {
        stringstream ss (chromo);
        int TASK, RANDOM_CORE, RANDOM_RATE; double Ui1;
        ss >> TASK >> Ui1 >> RANDOM_CORE >> RANDOM_RATE;

        do {
             c = rand()%4;
        }while (c == RANDOM_CORE);

        int NEW_CORE = c;

        do {
             r = rand()%5;
        }while(r == RANDOM_RATE);

        int NEW_RATE = r;

        string child;
        char buff[15];
        snprintf(buff, sizeof(buff),"%d %.2f %d %d", TASK, Ui1, NEW_CORE, NEW_RATE);
        child = string(buff);

        return child;
    }
    else

    {
        return chromo;
    }
}



//================================ TASK ALLOCATION - READ FILE ======================//

int ReadFile()
{

    string lineA;
    double x;
    ifstream infile;

    infile.open("fivenine.txt");

    if (!infile)
    {
        cout << "FAILED TO OPEN FILE!" << endl;
        return -1;
    }

    while(infile.good())
    {
        while(getline(infile, lineA))
        {
            istringstream streamA(lineA);
            rowA = 0;
            while(streamA >> x)
            {
                arrayA[colA][rowA] = x;
                rowA++;
            }

            colA++;
        }
    }
}


//======================= TASK ALLOCATION AMBFF =====================//

void MBFF( double *cs, double *ts, double *csr)
{

int cflag[CORE_NUM];  // speed setting of each core.
int allocation[CORE_NUM];
double cores[CORE_NUM];
int i, j;
int bnd;
bool stat;
double PrunT[CORE_NUM];
int task_location[TASK_NUM];
double newtotal = 0;
for (i = 0; i < CORE_NUM; i++)
{
    cs[i] = 0;
    cflag[i] = 0;
    PrunT[i] = 0;
    allocation[i] = -1;
}
    for (i = 0; i < TASK_NUM; i++)
    {
        T_UTIL[i] = ts[i];
        bnd = 0;
        for (j = 0; j < CORE_NUM; j++)
        {
            if((cs[j]+ts[i]) <= csr[bnd])
            {
                allocation[j] = allocation[j]+1;
                cs[j] = cs[j] + ts[i];
                cores[j] = csr[bnd];
                PrunT[j] = P[bnd];
                task_location[i] = j;
                break;
            }

            if (j == CORE_NUM - 1)
            {
                bnd = bnd + 1;
                if (bnd > 5)
                    cout << "FAIL"<< endl;
                j = -1;
             }
         }
     }

     for (int i = 0; i < CORE_NUM; i++)
     {
         C_UTIL[i] = cs[i];
         C_RATE[i] = cores[i];
     }

     for (int i = 0; i < TASK_NUM; i++)
        T_CORE_NUM[i] = task_location[i];

     for (int i = 0; i < CORE_NUM; i++)
        newtotal = newtotal + PrunT[i];

    cout << "AMBFF: " << newtotal << endl;
}



//============================= RESET THE GLOBAL VARIABLES ===================//
void resetter()
{
    for (int i = 0; i < CORE_NUM; i++)
    {
        C_UTIL[i] = 0;
        C_RATE[i] = 0;
    }

    for( int i = 0; i < TASK_NUM; i++)
    {
        T_UTIL[i] = 0;
        T_CORE_NUM[i] = 0;
    }
}






